<!--
 * @Description: 列表（数组）编辑表格（可增删改）
 * @Author: HuiSir<https://gitee.com/huisir001>
 * @Date: 2025-09-18 09:49:09
 * @LastEditTime: 2025-09-18 17:39:22
-->
<template>
  <div class="list-edit-table">
    <el-table :data="tableData" border style="width: 100%" size="small">
      <!-- 序号列 -->
      <el-table-column fixed="left" width="50" label="序号" align="center">
        <template #default="{ $index }">
          {{ $index + 1 }}
        </template>
      </el-table-column>

      <!-- 动态生成列 -->
      <el-table-column
        v-for="(column, index) in columns"
        :key="index"
        :prop="column.prop"
        :label="column.label"
        :width="column.width"
        :min-width="column.minWidth"
        :fixed="column.fixed"
      >
        <!-- 单元格插槽 -->
        <template v-if="column.slot" #default="scope">
          <slot :name="column.slot" v-bind="scope" />
        </template>
        <template v-else #default="{ row, $index }">
          <!-- 可编辑单元格 -->
          <template v-if="isEditableColumn(column.prop)">
            <!-- 输入框类型 -->
            <div v-if="column.editorType === 'input'" class="editable-cell">
              <el-input
                v-model="row[column.prop]"
                clearable
                :placeholder="`请输入${column.label}`"
                :ref="
                  (el: any) => setCellRef(el, $index, column.prop)
                "
              />
            </div>

            <!-- 下拉选择类型 -->
            <div
              v-else-if="column.editorType === 'select'"
              class="editable-cell"
            >
              <el-select
                v-model="row[column.prop]"
                clearable
                :placeholder="`请选择${column.label}`"
                :ref="
                  (el: any) => setCellRef(el, $index, column.prop)
                "
              >
                <el-option
                  v-for="item in column.options"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </div>

            <!-- 数字输入类型 -->
            <div
              v-else-if="column.editorType === 'number'"
              class="editable-cell"
            >
              <el-input-number
                v-model="row[column.prop]"
                :min="column.min || 0"
                :max="column.max || Infinity"
                :ref="
                  (el: any) => setCellRef(el, $index, column.prop)
                "
              />
            </div>

            <!-- 日期选择类型 -->
            <div v-else-if="column.editorType === 'date'" class="editable-cell">
              <el-date-picker
                v-model="row[column.prop]"
                type="date"
                :placeholder="`请选择${column.label}`"
                :ref="
                  (el: any) => setCellRef(el, $index, column.prop)
                "
              />
            </div>

            <!-- 默认显示 -->
            <span v-else>
              {{ row[column.prop] }}
            </span>
          </template>

          <!-- 不可编辑列 -->
          <span v-else>{{ row[column.prop] }}</span>
        </template>
      </el-table-column>

      <!-- 默认操作列 -->
      <el-table-column
        v-if="showActionColumn"
        label="操作"
        width="150"
        fixed="right"
      >
        <template #default="{ $index }">
          <el-button
            v-if="showSaveButton"
            type="primary"
            size="small"
            @click="handleSaveRow($index)"
          >
            保存
          </el-button>
          <el-popconfirm
            v-if="showDeleteButton"
            confirm-button-text="确认"
            cancel-button-text="取消"
            title="确认删除此行?"
            :hide-after="0"
            @confirm="handleDeleteRow($index)"
          >
            <template #reference>
              <el-button type="danger" size="small"> 删除 </el-button>
            </template>
          </el-popconfirm>
        </template>
      </el-table-column>
      <!-- 在表格最后添加一行作为新增按钮行 -->
      <template #append>
        <div style="display: flex; justify-content: center; padding: 8px 0">
          <el-button type="" @click="handleAddRow" :icon="Plus" link>
            新增一行
          </el-button>
        </div>
      </template>
    </el-table>

    <!-- 底部操作按钮 -->
    <div class="action-buttons">
      <el-button type="success" @click="handleSaveAll" v-if="showSaveAllButton">
        保存全部
      </el-button>
    </div>
  </div>
</template>

<script lang="ts" setup>
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ref, watch, nextTick, computed, PropType } from "vue";
import { Plus } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import { ListEditTableCol, ListEditTableProps } from "../types";

// 组件名称
defineOptions({ name: "FyListEditTable" });

const props = withDefaults(defineProps<ListEditTableProps>(), {
  columns: () => [],
  data: () => [],
  showActionColumn: true,
  showSaveButton: true,
  showDeleteButton: true,
  showSaveAllButton: true,
  addRowPosition: "bottom" as const,
});

/**
 * 表格数据
 */
const listData = defineModel("data", {
  type: Object as PropType<Array<Record<string, any>>>,
  required: true,
});

const emit = defineEmits<{
  (e: "save-row", row: Record<string, any>, index: number): void;
  (e: "save-all", data: Array<Record<string, any>>): void;
  (e: "delete-row", index: number): void;
  (e: "add-row"): void;
}>();

const tableData = ref<Array<Record<string, any>>>([...props.data]);
const errors = ref<Record<number, Record<string, string>>>({}); // 存储所有行的错误信息
const cellRefs = ref<Record<string, HTMLElement | null>>({}); // 存储单元格元素引用

// 缓存列配置映射，避免重复查找
const columnsMap = computed(() => {
  const map = new Map<string, ListEditTableCol>();
  props.columns.forEach((col) => map.set(col.prop, col));
  return map;
});

// 判断列是否可编辑
const isEditableColumn = (prop: string): boolean => {
  const column = columnsMap.value.get(prop);
  return column?.editable === true;
};

// 验证单个单元格
const validateCell = (row: Record<string, any>, prop: string) => {
  const column = columnsMap.value.get(prop);
  if (!column || !column.rules) {
    setCellError(row, prop, "");
    return true;
  }

  const value = row[prop];
  let isValid = true;

  if (!Array.isArray(column.rules) && typeof column.rules === "object") {
    column.rules = [column.rules];
  }

  for (const rule of column.rules) {
    // 必填验证
    if (
      rule.required &&
      (value === undefined || value === null || value === "")
    ) {
      setCellError(row, prop, rule.message || `${column.label}不能为空`);
      isValid = false;
      break;
    }

    // 最小长度验证（适用于字符串）
    if (rule.min && typeof value === "string" && value.length < rule.min) {
      setCellError(
        row,
        prop,
        rule.message || `${column.label}长度不能小于${rule.min}`
      );
      isValid = false;
      break;
    }

    // 最大长度验证（适用于字符串）
    if (rule.max && typeof value === "string" && value.length > rule.max) {
      setCellError(
        row,
        prop,
        rule.message || `${column.label}长度不能大于${rule.max}`
      );
      isValid = false;
      break;
    }

    // 最小值验证（适用于数字）
    if (
      rule.min !== undefined &&
      typeof value === "number" &&
      value < rule.min
    ) {
      setCellError(
        row,
        prop,
        rule.message || `${column.label}不能小于${rule.min}`
      );
      isValid = false;
      break;
    }

    // 最大值验证（适用于数字）
    if (
      rule.max !== undefined &&
      typeof value === "number" &&
      value > rule.max
    ) {
      setCellError(
        row,
        prop,
        rule.message || `${column.label}不能大于${rule.max}`
      );
      isValid = false;
      break;
    }

    // 正则验证
    if (
      rule.pattern &&
      typeof value === "string" &&
      !rule.pattern.test(value)
    ) {
      setCellError(row, prop, rule.message || `${column.label}格式不正确`);
      isValid = false;
      break;
    }

    // 自定义验证函数
    if (rule.validator) {
      const result = rule.validator(value);
      if (typeof result === "string") {
        setCellError(row, prop, result);
        isValid = false;
        break;
      } else if (result === false) {
        setCellError(row, prop, rule.message || `${column.label}验证失败`);
        isValid = false;
        break;
      }
    }
  }

  if (isValid) {
    setCellError(row, prop, "");
  }

  return isValid;
};

// 验证整行并返回第一个错误字段和错误消息
const validateRowAndGetFirstError = (
  row: Record<string, any>,
  rowIndex?: number
) => {
  let firstErrorField: string | undefined = undefined;
  let firstErrorMessage: string | undefined = undefined;
  let isValid = true;

  props.columns.forEach((column) => {
    if (column.editable && column.prop in row) {
      const result = validateCell(row, column.prop);
      if (!result && !firstErrorField) {
        firstErrorField = column.prop;
        // 如果传入了rowIndex，直接使用，避免重复查找
        const index =
          rowIndex !== undefined
            ? rowIndex
            : tableData.value.findIndex((r) => r === row);
        if (index !== -1) {
          firstErrorMessage = errors.value[index]?.[column.prop];
        }
        isValid = false;
      }
    }
  });

  return { isValid, firstErrorField, firstErrorMessage };
};

// 验证所有行
const validateAllRows = (): {
  isValid: boolean;
  firstErrorRow?: number;
  firstErrorField?: string;
  firstErrorMessage?: string;
} => {
  for (let i = 0; i < tableData.value.length; i++) {
    // 直接传入行索引，避免内部重复查找
    const { isValid, firstErrorField, firstErrorMessage } =
      validateRowAndGetFirstError(tableData.value[i], i);
    if (!isValid) {
      return {
        isValid: false,
        firstErrorRow: i,
        firstErrorField,
        firstErrorMessage,
      };
    }
  }
  return { isValid: true };
};

// 设置单元格引用
const setCellRef = (el: any, rowIndex: number, prop: string) => {
  if (el) {
    // 对于Element Plus组件，需要获取其内部的input元素
    const actualEl = el.$el || el;
    cellRefs.value[`${rowIndex}_${prop}`] = actualEl;

    // 如果已经有错误信息，立即应用错误样式
    nextTick(() => {
      if (errors.value[rowIndex]?.[prop]) {
        applyErrorStyle(rowIndex, prop, true);
      }
    });
  }
};

// 应用或移除错误样式
const applyErrorStyle = (rowIndex: number, prop: string, hasError: boolean) => {
  const key = `${rowIndex}_${prop}`;
  const element = cellRefs.value[key];

  if (element?.classList) {
    if (hasError) {
      element.classList.add("is-error");
    } else {
      element.classList.remove("is-error");
    }
  }
};

// 设置单元格错误信息
const setCellError = (
  row: Record<string, any>,
  prop: string,
  message: string
) => {
  const rowIndex = tableData.value.findIndex((r) => r === row);
  if (rowIndex === -1) return;

  if (!errors.value[rowIndex]) {
    errors.value[rowIndex] = {};
  }

  const hasError = !!message;
  errors.value[rowIndex][prop] = message;

  // 直接操作DOM应用错误样式
  applyErrorStyle(rowIndex, prop, hasError);
};

// 保存行
const handleSaveRow = (index: number) => {
  const row = tableData.value[index];
  // 直接传入行索引，避免内部重复查找
  const { isValid, firstErrorField, firstErrorMessage } =
    validateRowAndGetFirstError(row, index);

  if (isValid) {
    emit("save-row", row, index);
  } else {
    if (firstErrorMessage) {
      ElMessage.error(firstErrorMessage);
    } else if (firstErrorField) {
      const column = columnsMap.value.get(firstErrorField);
      ElMessage.error(`${column?.label}填写有误，请检查`);
    }
  }
};

// 保存全部
const handleSaveAll = () => {
  const { isValid, firstErrorRow, firstErrorField, firstErrorMessage } =
    validateAllRows();

  if (isValid) {
    emit("save-all", tableData.value);
  } else {
    if (firstErrorRow !== undefined && firstErrorMessage) {
      ElMessage.error(firstErrorMessage);
    } else if (firstErrorRow !== undefined && firstErrorField) {
      const column = columnsMap.value.get(firstErrorField);
      ElMessage.error(
        `第${firstErrorRow + 1}行${column?.label}填写有误，请检查`
      );
    }
  }
};

// 删除行
const handleDeleteRow = (index: number) => {
  tableData.value.splice(index, 1);

  // 清理已删除行的错误信息
  const newErrors = { ...errors.value };
  delete newErrors[index];
  errors.value = newErrors;

  // 清理相关的DOM引用 - 优化实现
  Object.keys(cellRefs.value).forEach((key) => {
    if (key.startsWith(`${index}_`)) {
      delete cellRefs.value[key];
    }
  });

  listData.value = tableData.value;
  emit("delete-row", index);
};

// 新增行
const handleAddRow = () => {
  const newRow: Record<string, any> = {};

  props.columns.forEach((col) => {
    if (col.editable) {
      switch (col.editorType) {
        case "select":
          newRow[col.prop] = col.options?.[0]?.value || "";
          break;
        case "number":
          newRow[col.prop] = col.min || 0;
          break;
        case "date":
          newRow[col.prop] = new Date();
          break;
        default:
          newRow[col.prop] = "";
      }
    }
  });

  tableData.value.push(newRow);
  listData.value = tableData.value;
  emit("add-row");
};

watch(
  () => props.data,
  (newVal) => {
    tableData.value = [...newVal];
    // 初始化错误信息
    errors.value = {};
    // 清空DOM引用
    cellRefs.value = {};
  },
  { deep: true }
);

defineExpose({
  addRow: handleAddRow,
  saveAll: handleSaveAll,
});
</script>

<style scoped lang="scss">
.action-buttons {
  margin-top: 15px;
  text-align: center;
}

.editable-cell {
  position: relative;
  width: 100%;
  box-sizing: border-box;
  line-height: normal;
}

// 统一设置所有输入组件的尺寸
.editable-cell :deep(.el-input),
.editable-cell :deep(.el-select),
.editable-cell :deep(.el-input-number),
.editable-cell :deep(.el-date-editor) {
  width: 100%;
  box-sizing: border-box;
  height: 30px;
  line-height: 30px;
}

// 统一设置所有输入组件的包装器尺寸
.editable-cell :deep(.el-input__wrapper),
.editable-cell :deep(.el-select .el-input__wrapper),
.editable-cell :deep(.el-select .el-select__wrapper),
.editable-cell :deep(.el-input-number .el-input__wrapper),
.editable-cell :deep(.el-date-editor .el-input__wrapper) {
  height: 30px !important;
  min-height: 30px !important;
  line-height: 30px !important;
}

// 统一设置所有输入框内部元素尺寸
.editable-cell :deep(.el-input__inner),
.editable-cell :deep(.el-select .el-input__inner),
.editable-cell :deep(.el-input-number .el-input__inner),
.editable-cell :deep(.el-date-editor .el-input__inner) {
  height: 30px !important;
  line-height: 30px !important;
  min-height: 30px !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
}

// 错误状态样式
.list-edit-table .is-error :deep(.el-input__wrapper),
.list-edit-table .is-error :deep(.el-select__wrapper) {
  box-shadow: 0 0 0 1px #f56c6c inset !important;
}

.error-message {
  color: #f56c6c;
  font-size: 12px;
  line-height: 1;
  padding-top: 4px;
  position: absolute;
  left: 0;
}

.list-edit-table :deep(.el-table__empty-block) {
  display: none;
}
</style>
